package platformer;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;

import javax.swing.JFrame;
import javax.swing.UIManager;

@SuppressWarnings("serial")
public class Platformer extends JFrame
{

	boolean drawLine = false;
	boolean running = false;
	int cooldown = 15;
	int frames = 0;
	final int MOUSE_LEFT = 1, MOUSE_MIDDLE = 2, MOUSE_RIGHT = 3;
	final int WIN_X = 1024, WIN_Y = 768;
	final float FPS = 60;
	long time = 0;

	BufferedImage backBuffer;
	CollisionMonitor collisionMonitor;
	Input input;
	Insets insets;
	List<Bullet> bullets;
	List<Enemy> enemies;
	List<Drawable> drawablesLate;
	List<Drawable> ignore;
	List<Drawable> temp;
	Player player;
	Point mouseCoords;
	Random rand;
	Set<Drawable> drawables;


	public static void main(String[] args)
	{
		Platformer game = new Platformer();
		game.run();
		System.exit(0);
	}


	/**
	 * Main loop for the game
	 */
	public void run()
	{
		initialize();

		while (running)
		{
			time = System.currentTimeMillis();

			update();
			draw();

			try {	Thread.sleep((long) ((1000 / FPS) - (System.currentTimeMillis() - time)));	} catch (Exception e) {	} // Forces the loop to slow down so it is limited to a certain amount of loops per second.
			frames++;
		}
	}
	/**
	 * Called once at the very beginning of the program. Sets everything up so that run() can do the rest.
	 */
	void initialize()
	{
		setTitle("Platformer - Brian Cohen (INT_COMPSCI)");
		setResizable(false);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		try {	UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());	} catch (Exception e) {	} // Gets rid of the stupid looking Java buttons and replaces them with the normal windows ones.
		setVisible(true);

		backBuffer = new BufferedImage(WIN_X, WIN_Y, BufferedImage.TYPE_INT_RGB);
		bullets = new ArrayList<Bullet>();
		drawables = new HashSet<Drawable>();
		drawablesLate = new ArrayList<Drawable>();
		collisionMonitor = new CollisionMonitor(new HashSet<Drawable>());
		enemies = new ArrayList<Enemy>();
		ignore = new ArrayList<Drawable>();
		input = new Input(this);
		insets = getInsets(); 
		mouseCoords = new Point(0,0);
		player = new Player(0, 0, 32, 32, 5, Color.blue);
		rand = new Random();
		setSize(insets.left + WIN_X + insets.right, insets.top + WIN_Y + insets.bottom); // Adjusts size of window to account for the title bar, etc.
		temp = new ArrayList<Drawable>();
		ignore.add(player);

		createEnemies(10);

		running = true;
	}
	/**
	 * Called once every frame. Used to make changes to anything during run(). No real drawing is done here.
	 */
	void update()
	{
		mouseCoords = new Point(MouseInfo.getPointerInfo().getLocation().x - this.getX(), MouseInfo.getPointerInfo().getLocation().y - this.getY() - 16);


		for (Bullet b : bullets)
		{
			if ((b.ttl <= 0 || b.x > WIN_X || b.y > WIN_Y || b.x < 0 || b.y < 0) && !drawablesLate.contains(b))
			{
				drawablesLate.add(b);				//bullets that have been alive longer than 3 seconds or are outside the screen are marked for deletion.
			}
			else
			{
				b.move();
				b.ttl--;
			}
		}

		drawables.clear();
		drawables.addAll(bullets);
		drawables.add(player);
		drawables.addAll(enemies);
		
		if (bullets != null && !bullets.isEmpty())	temp.addAll(bullets);
		if (enemies != null && !bullets.isEmpty())	temp.addAll(enemies);
		if (temp != null && !temp.isEmpty()) 
		{
			collisionMonitor.setDrawables(new HashSet<Drawable>(drawables));
			try
			{
				for (Collision c : collisionMonitor.getCollisions(ignore))
				{
					if (c.drawable1.getClass() != Bullet.class)
					{
						drawablesLate.add(c.drawable1);
					}
					if (c.drawable2.getClass() != Bullet.class)
					{
					drawablesLate.add(c.drawable2);
					}
				}
			}	catch(Exception e){}
		}

		if (drawablesLate != null)
		{
			for (Drawable s : drawablesLate)
			{
				drawables.remove(s);//remove drawables marked for deletion.
			}
		}

		/*			Input			*/

		if (input.isMousePressed(MOUSE_LEFT) && cooldown <= 0)
		{
			bullets.add(new Bullet(player.getCenterX(), player.getCenterY(), mouseCoords, Color.black));
			cooldown = 15;
		}

		if (input.isKeyPressed(KeyEvent.VK_W))
		{
			player.y -= player.speed;
		}

		if (input.isKeyPressed(KeyEvent.VK_A))
		{
			player.x -= player.speed;
		}

		if (input.isKeyPressed(KeyEvent.VK_S))
		{
			player.y += player.speed;
		}

		if (input.isKeyPressed(KeyEvent.VK_D))
		{
			player.x += player.speed;
		}

		cooldown--;
	}
	/**
	 * Called once every frame. Redraws everything that has been moved by update().
	 */
	void draw()
	{
		Graphics g = getGraphics();
		Graphics2D bbg = (Graphics2D) backBuffer.getGraphics(); // If backBuffer is not used, the animation will be choppy and look bad.
		bbg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // Makes the images less jagged.


		bbg.setColor(Color.white); 
		bbg.fillRect(0, 0, WIN_X, WIN_Y); //Background
		
		for (Drawable s : drawables)
		{
			bbg.setColor(s.getColor());
			bbg.fill(s);
		}


		g.drawImage(backBuffer, insets.left, insets.top, this);
	}


	public void createEnemies(int count)
	{
		for (int i = 0; i < count; i++)
		{
			enemies.add(new Enemy(rand.nextDouble() * WIN_X, rand.nextDouble() * WIN_Y, 32, 32, rand.nextDouble() * 2, Color.red)); 
		}
	}
	//TODO Fix draw() so that it draws everything in the List<Drawable> drawables.
}